package com.tengfei.main.course.service.impl;


import com.tengfei.common.utils.DateUtils;
import com.tengfei.common.utils.file.FileUtils;
import com.tengfei.main.course.config.AliyunConfig;
import com.tengfei.main.course.config.FfmPegConfig;
import com.tengfei.main.course.domain.Video;
import com.tengfei.main.course.mapper.VideoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author feige
 * @version v1.0
 * @date 2022-07-23-16:40
 * @description
 */

@Slf4j
@Component
public class ConvertM3U8 {

    @Resource
    private FfmPegConfig ffmPegConfig;
    @Resource
    private OssServiceImpl ossService;
    @Resource
    private AliyunConfig aliyunConfig;
    // 导入video的mapper，不导入service防止循环依赖
    @Resource
    private VideoMapper videoMapper;


    /**
     * ffmPeg程序转换m3u8
     * ffmPeg -i vue.mp4 -c:v libx264 -hls_time 20 -hls_list_size 0 -c:a aac -strict -2 -f hls vue.m3u8
     * ffmPeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
     *
     * @param inFile      文件全路径
     * @param outFile     输出文件路径
     * @param outFileName m3u8文件输出名称
     * @return
     */
    @Async
    public void processM3U8(String inFile, String outFile, String outFileName, Long videoId) {
        // 目录不存在，创建目录
        File file = new File(ffmPegConfig.getOutSrc() + outFile);
        if (!file.exists()) {
            file.mkdirs();
        }
        //这里就写入执行语句就可以了
        List<String> commend = new ArrayList<>();
        // ffmpeg程序地址
        commend.add("ffmpeg程序地址");
        // 源mp4视频
        commend.add("-i");
        commend.add(inFile);
        // 获取视频第一秒的第一帧为视频封面
        commend.add("-ss");
        commend.add("00:00:01");
        commend.add("-frames:v");
        commend.add("1");
        commend.add(ffmPegConfig.getOutSrc() + outFile + outFileName + ".png");
        // 视频使用h264编码
        commend.add("-c:v");
        commend.add("libx264");
        // 音频使用aac解码
        commend.add("-c:a");
        commend.add("aac");
        // 切片大小
        commend.add("-hls_time");
        commend.add(ffmPegConfig.getSize());
        // 生成ts切片文件限制，0:无限制
        commend.add("-hls_list_size");
        commend.add("0");
        commend.add("-strict");
        commend.add("-2");
        commend.add("-f");
        commend.add("hls");
        commend.add("-s");
        commend.add("1920x1080");
        // 启用线程加速
        commend.add("-threads");
        commend.add(ffmPegConfig.getThreads());
        commend.add("-preset");
        commend.add("ultrafast");
        // 生成m3u8文件
        commend.add(ffmPegConfig.getOutSrc() + outFile + outFileName + ".m3u8");
        try {
            ProcessBuilder builder = new ProcessBuilder();//java
            builder.command(commend);
            Process p = builder.start();
            int i = doWaitFor(p);
            log.info("***i=【{}】***", i);
            log.info("视频转码完成");

            File m3u8file = new File(ffmPegConfig.getOutSrc() + outFile);
            File[] files = m3u8file.listFiles();
            for (File f : files) {
                if (!f.isDirectory()) {
                    String videoPath = new StringBuilder(aliyunConfig.getRootPath())
                            .append("/")
                            .append("video")
                            .append("/")
                            .append(outFile)
                            .append(f.getName())
                            .toString();
                    // 上传文件到oss
                    ossService.uploadVideo(videoPath, f);
                }
            }

            p.destroy();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 删除temp中的视频
        FileUtils.delDirectory(new File(ffmPegConfig.getTemp() + outFileName));
        // 读取outFile中的封面图片，上传道对象存储
        File coverImg = new File(ffmPegConfig.getOutSrc() + outFile + outFileName + ".png");
        String filePath = new StringBuilder(aliyunConfig.getRootPath())
                .append("/")
                .append("resources")
                .append("/")
                .append(DateUtils.datePath())
                .append("/")
                .append(outFileName)
                .append(".")
                .append("png")
                .toString();
        try (FileInputStream fileInputStream = new FileInputStream(coverImg)) {
            ossService.uploadImg(filePath, fileInputStream);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        // 更新视频数据库参数，更新视频为转码完成
        Video video = new Video();
        video.setVideoId(videoId);
        video.setVideoCover(aliyunConfig.getUrlPrefix() + filePath);
        video.setVideoStatus(1);
        videoMapper.updateById(video);
    }

    /**
     * 监听ffmPeg运行过程
     *
     * @param p 进程
     * @return 直接结果
     */
    private int doWaitFor(Process p) {
        InputStream in = null;
        InputStream err = null;
        int exitValue = -1; // returned to caller when p is finished
        try {
            log.info("***检测ffmPeg运行***");
            log.info("视频开始转码.....");
            in = p.getInputStream();
            err = p.getErrorStream();
            boolean finished = false; // Set to true when p is finished
            while (!finished) {
                try {
                    while (in.available() > 0) {
                        Character c = (char) in.read();
//                        log.info(c.toString());
                        System.out.print(c);
                    }
                    while (err.available() > 0) {
                        Character c = (char) err.read();
//                        log.info(c.toString());
                        System.out.print(c);
                    }

                    exitValue = p.exitValue();
                    finished = true;

                } catch (IllegalThreadStateException e) {
                    Thread.sleep(500);
                }
            }
        } catch (Exception e) {
            log.error("doWaitFor();: unexpected exception - "
                    + e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("异常：{}", e);
            }
            if (err != null) {
                try {
                    err.close();
                } catch (IOException e) {
                    log.error("异常：{}", e);
                }
            }
        }
        return exitValue;
    }
}
